/*
 * Copyright 2019 Flipkart Internet Pvt. Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flipkart.ohos.proteus;

import ohos.agp.components.ComponentContainer;

import com.flipkart.ohos.proteus.exceptions.ProteusInflateException;
import com.flipkart.ohos.proteus.value.Layout;
import com.flipkart.ohos.proteus.value.ObjectValue;
import com.flipkart.ohos.proteus.value.Value;

import java.util.Iterator;

/**
 * A layout builder which can parse json to construct an view out of it. It uses the
 * registered parsers to convert the json string to a view and then assign attributes.
 */
public class SimpleLayoutInflater implements ProteusLayoutInflater {
    private static final String TAG = "SimpleLayoutInflater";

    protected final ProteusContext context;

    protected final IdGenerator idGenerator;

    SimpleLayoutInflater(ProteusContext context, IdGenerator idGenerator) {
        this.context = context;
        this.idGenerator = idGenerator;
    }

    @Override
    public ViewTypeParser getParser(String type) {
        return context.getParser(type);
    }

    @Override
    public ProteusView inflate(Layout layout, ObjectValue data, ComponentContainer parent, int dataIndex) {
        /*
         * Get the the view type parser for this layout type
         */
        ViewTypeParser viewTypeParser = null;
        if (layout != null) {
            viewTypeParser = getParser(layout.type);
        }
        if (viewTypeParser == null && layout != null) {
            /*
             * If parser is not registered ask the application land for the view
             */
            return onUnknownViewEncountered(layout.type, layout, data, dataIndex);
        }

        /*
         * Create a view of {@code layout.type}
         */
        final ProteusView view = createView(viewTypeParser, layout, data, parent, dataIndex);

        if (view.getViewManager() == null) {
            /*
             * Do post creation logic
             */
            onAfterCreateView(viewTypeParser, view, parent, dataIndex);

            /*
             * Create View Manager for {@code layout.type}
             */
            final ProteusView.Manager viewManager = createViewManager(viewTypeParser,
                view, layout, data, parent, dataIndex);

            /*
             * Set the View Manager on the view.
             */
            view.setViewManager(viewManager);
        }

        /*
         * Handle each attribute and set it on the view.
         */
        if (layout.attributes != null) {
            Iterator<Layout.Attribute> iterator = layout.attributes.iterator();
            Layout.Attribute attribute;
            while (iterator.hasNext()) {
                attribute = iterator.next();
                handleAttribute(viewTypeParser, view, attribute.id, attribute.value);
            }
        }
        return view;
    }

    @Override
    public ProteusView inflate(Layout layout, ObjectValue data, int dataIndex) {
        return inflate(layout, data, null, dataIndex);
    }

    @Override
    public ProteusView inflate(Layout layout, ObjectValue data) {
        return inflate(layout, data, null, -1);
    }

    @Override
    public ProteusView inflate(String name, ObjectValue data, ComponentContainer parent, int dataIndex) {
        Layout layout = context.getLayout(name);
        return inflate(layout, data, parent, dataIndex);
    }

    @Override
    public ProteusView inflate(String name, ObjectValue data, int dataIndex) {
        return inflate(name, data, null, dataIndex);
    }

    @Override
    public ProteusView inflate(String name, ObjectValue data) {
        return inflate(name, data, null, -1);
    }

    @Override
    public int getUniqueViewId(String id) {
        return idGenerator.getUnique(id);
    }

    @Override
    public IdGenerator getIdGenerator() {
        return idGenerator;
    }

    protected ProteusView createView(ViewTypeParser parser, Layout layout, ObjectValue data,
                                     ComponentContainer parent, int dataIndex) {
        if (parser != null) {
            return parser.createView(context, layout, data, parent, dataIndex);
        }
        return null;
    }

    protected ProteusView.Manager createViewManager(ViewTypeParser parser, ProteusView view, Layout layout,
                                                    ObjectValue data, ComponentContainer parent, int dataIndex) {
        return parser.createViewManager(context, view, layout, data, parser, parent, dataIndex);
    }

    protected void onAfterCreateView(ViewTypeParser parser, ProteusView view, ComponentContainer parent, int index) {
        parser.onAfterCreateView(view, parent, index);
    }

    protected ProteusView onUnknownViewEncountered(String type, Layout layout, ObjectValue data, int dataIndex) {
        if (ProteusConstants.isLoggingEnabled()) {
            //Log.d(TAG, "No ViewTypeParser for: " + type);
        }
        if (context.getCallback() != null) {
            ProteusView view = context.getCallback().onUnknownViewType(context, type, layout, data, dataIndex);
            //noinspection ConstantConditions because we need to throw a ProteusInflateException specifically
            if (view == null) {
                try {
                    throw new ProteusInflateException("inflater Callback#onUnknownViewType() must not return null");
                } catch (ProteusInflateException e) {
                    e.printStackTrace();
                }
            }
        }
        try {
            throw new ProteusInflateException("Layout contains type: 'include' but inflater callback is null");
        } catch (ProteusInflateException e) {
            e.printStackTrace();
            return null;
        }
    }

    protected boolean handleAttribute(ViewTypeParser parser, ProteusView view, int attribute, Value value) {
        if (ProteusConstants.isLoggingEnabled()) {
            LogUtil.debug(TAG, "Handle '" + attribute + "' : " + value);
        }
        //noinspection unchecked
        return parser.handleAttribute(view.getAsView(), attribute, value);
    }
}
